package cn.sylinx.horm.spring.boot;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

import cn.sylinx.horm.config.DataSourceConfig;
import cn.sylinx.horm.config.OrmConfig;
import cn.sylinx.horm.config.OrmConfigHolder;
import cn.sylinx.horm.config.ServiceEnvironment;
import cn.sylinx.horm.config.SingleDataSourceConfig;
import cn.sylinx.horm.config.specific.SpecificConfig;
import cn.sylinx.horm.config.specific.SpecificConfigHolder;
import cn.sylinx.horm.core.DynamicClient;
import cn.sylinx.horm.core.OrmClient;
import cn.sylinx.horm.core.SqlClient;
import cn.sylinx.horm.core.TransactionSupportOrmClient;
import cn.sylinx.horm.core.datasource.ConnectionProvider;
import cn.sylinx.horm.core.datasource.NamedDataSource;
import cn.sylinx.horm.dialect.DbType;
import cn.sylinx.horm.dialect.DialectFactory;
import cn.sylinx.horm.exception.HORMException;
import cn.sylinx.horm.resource.parse.DefaultSqlParser;
import cn.sylinx.horm.resource.parse.SqlParser;
import cn.sylinx.horm.spring.transaction.SpringDataSourceConnectionProvider;
import cn.sylinx.horm.transaction.jdbc.JdbcTransactionalConnectionProvider;
import cn.sylinx.horm.util.GLog;
import cn.sylinx.horm.util.StrKit;

/**
 * 使用spring数据源初始化
 * 
 * @author johnhan
 *
 */
class SqlClientInitBySpring {

    private HORMBootConfig bootConfig;

    private ApplicationContext applicationContext;

    public SqlClient create() {
        initConfig();
        return parseSpringDatasource(bootConfig);
    }

    private void initConfig() {
        OrmConfigHolder.init(ServiceEnvironment.SPRINGBOOT, bootConfig);
        Map<String, SpecificConfig> specificConfigMaps = new HashMap<>();
        OrmConfig ormConfig = OrmConfigHolder.getOrmConfig();
        specificConfigMaps.put(DynamicClient.DEFAULT_DS_NAME, copyFromOrmConfig(ormConfig));

        if (!isSingle(bootConfig)) {
            // 多数据源
            String defaultSqlClientName = null;
            List<SingleDataSourceConfig> multids = bootConfig.getDatasource().getMultids();
            if (multids == null || multids.isEmpty()) {
                throw new HORMException("多数据源配置异常");
            }

            for (SingleDataSourceConfig sds : multids) {

                if (sds.getPrimary() != null && sds.getPrimary()) {
                    defaultSqlClientName = sds.getName();
                }

                SpecificConfig c = copyFromOrmConfig(ormConfig);
                SpecificConfig sconfig = sds.getSpecificConfig();
                if (sconfig != null) {
                    if (sconfig.getSqlStatOpen() != null) {
                        c.setSqlStatOpen(sconfig.getSqlStatOpen());
                    }
                    if (sconfig.getSqlClientInterceptorEnable() != null) {
                        c.setSqlClientInterceptorEnable(sconfig.getSqlClientInterceptorEnable());
                    }
                }

                specificConfigMaps.put(sds.getName(), c);
            }
            if (defaultSqlClientName == null) {
                defaultSqlClientName = multids.get(0).getName();
            }
            specificConfigMaps.put(DynamicClient.DEFAULT_DS_NAME, specificConfigMaps.get(defaultSqlClientName));
        }

        SpecificConfigHolder.init(bootConfig, specificConfigMaps);
    }

    private SpecificConfig copyFromOrmConfig(OrmConfig ormConfig) {
        SpecificConfig sc = new SpecificConfig();
        sc.setSqlStatOpen(ormConfig.isSqlStatOpen());
        sc.setSqlClientInterceptorEnable(true);
        return sc;
    }

    public SqlClientInitBySpring setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        return this;
    }

    public SqlClientInitBySpring setBootConfig(HORMBootConfig bootConfig) {
        this.bootConfig = bootConfig;
        return this;
    }

    private SqlClient parseSpringDatasource(HORMBootConfig bootConfig) {
        if (isSingle(bootConfig)) {
            GLog.debug("初始化单数据源 ==>> By Spring");
            initSingleSqlClient(bootConfig);
        } else {
            // 多数据源初始化
            GLog.debug("初始化多数据源 ==>> By Spring");
            initMultiSqlClient(bootConfig);
        }
        return SqlClient.getDefaultClient();
    }

    private void initSingleSqlClient(HORMBootConfig bootConfig, String dbtype, String qualifier,
            String dataSourceName) {

        DbType dbTypeEnum = DbType.getDbType(dbtype);
        DataSource dataSource = StrKit.isBlank(qualifier) ? applicationContext.getBean(DataSource.class)
                : applicationContext.getBean(qualifier, DataSource.class);

        NamedDataSource namedDataSource = new NamedDataSource();
        namedDataSource.setDataSource(dataSource);
        namedDataSource.setDbType(dbTypeEnum);
        namedDataSource.setDataSourceName(dataSourceName);

        OrmClient sqlClient = getSqlClient(bootConfig);
        sqlClient.setDialect(DialectFactory.createDialect(dbtype));
        sqlClient.setSqlParser(getSqlParser(bootConfig, dbTypeEnum));
        // 注册
        sqlClient.setConnectionProvider(getConnectionProvider(bootConfig, namedDataSource));

        // 注册sqlClient
        DynamicClient.register(sqlClient);
    }

    private void initOneDatasource(HORMBootConfig bootConfig, SingleDataSourceConfig singleDataSourceConfig) {
        initSingleSqlClient(bootConfig, singleDataSourceConfig.getDbtype(), singleDataSourceConfig.getName(),
                singleDataSourceConfig.getName());
    }

    private boolean isSingle(HORMBootConfig bootConfig) {
        DataSourceConfig dataSourceConfig = bootConfig.getDatasource();
        if (dataSourceConfig == null) {
            throw new HORMException("数据源配置丢失");
        }
        return !dataSourceConfig.isMultiple();
    }

    private ConnectionProvider getConnectionProvider(HORMBootConfig bootConfig, NamedDataSource namedDataSource) {
        return bootConfig.isTransactionNative() ? new JdbcTransactionalConnectionProvider(namedDataSource)
                : new SpringDataSourceConnectionProvider(namedDataSource);
    }

    private OrmClient getSqlClient(HORMBootConfig bootConfig) {
        return bootConfig.isTransactionNative() ? new TransactionSupportOrmClient() : new OrmClient();
    }

    private SqlParser getSqlParser(HORMBootConfig bootConfig, DbType dbTypeEnum) {
        return new DefaultSqlParser(bootConfig.getSqlPostfix(), bootConfig.isParseSqlPathDbtype() ? dbTypeEnum : null);
    }

    private void initSingleSqlClient(HORMBootConfig bootConfig) {
        initSingleSqlClient(bootConfig, bootConfig.getDatasource().getDefaultDbtype(), null,
                DynamicClient.DEFAULT_DS_NAME);
    }

    private void initMultiSqlClient(HORMBootConfig bootConfig) {
        List<SingleDataSourceConfig> multids = bootConfig.getDatasource().getMultids();
        if (multids == null || multids.isEmpty()) {
            throw new HORMException("多数据源初始化异常");
        }
        SingleDataSourceConfig defaultDataSource = null;
        int primaryMult = 0;

        for (SingleDataSourceConfig c : multids) {

            initOneDatasource(bootConfig, c);

            if (c.getPrimary() != null && c.getPrimary()) {
                defaultDataSource = c;
                primaryMult++;
            }

            if (primaryMult > 1) {
                throw new HORMException("Primary Datasource should be only one!");
            }
        }
        if (defaultDataSource == null) {
            defaultDataSource = multids.get(0);
        }

        // 初始化单数据源
        initSingleSqlClient(bootConfig, defaultDataSource.getDbtype(), defaultDataSource.getName(),
                DynamicClient.DEFAULT_DS_NAME);
    }
}
